home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 16
/
Aminet 16 (1996)(GTI - Schatztruhe)[!][Dec 1996].iso
/
Aminet
/
comm
/
term
/
term_source.lha
/
Extras
/
Source
/
term-source.lha
/
Accountant.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-10-21
|
11KB
|
517 lines
/*
** Accountant.c
**
** Connection cost accounting
**
** Copyright © 1990-1996 by Olaf `Olsen' Barthel
** All Rights Reserved
**
** :ts=4
*/
#ifndef _GLOBAL_H
#include "Global.h"
#endif
/****************************************************************************/
enum { STATE_Idle, STATE_Waiting };
/****************************************************************************/
enum { ACMSG_Start,ACMSG_Stop,ACMSG_Query };
typedef struct AccountantMsg
{
struct Message Message;
UWORD Type;
struct timeval Time;
} AccountantMsg;
/****************************************************************************/
STATIC struct MsgPort *AccountantPort;
STATIC struct Task *AccountantTask;
STATIC struct SignalSemaphore AccountantSemaphore;
STATIC ULONG AccountantCost;
/****************************************************************************/
/* AccountantEntry(VOID):
*
* The rates accounting task.
*/
STATIC VOID SAVE_DS
AccountantEntry(VOID)
{
struct MsgPort *TimePort;
/* Set up the timer resources */
if(TimePort = CreateMsgPort())
{
struct timerequest *TimeRequest;
if(TimeRequest = (struct timerequest *)CreateIORequest(TimePort,sizeof(struct timerequest)))
{
if(!OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)TimeRequest,NULL))
{
/* Now create the communications port */
if(AccountantPort = CreateMsgPort())
{
struct timeval StopTime;
struct timeval Now;
ULONG Signals;
WORD State;
/* That's me */
AccountantTask = FindTask(NULL);
/* Wake up the main program */
Signal((struct Task *)ThisProcess,SIG_HANDSHAKE);
/* Doing nothing yet... */
State = STATE_Idle;
FOREVER
{
/* Wait for a sign */
Signals = Wait(PORTMASK(TimePort) | PORTMASK(AccountantPort) | SIG_KILL);
/* Terminate this task? */
if(Signals & SIG_KILL)
break;
/* The timer has elapsed? */
if((Signals & PORTMASK(TimePort)) && State == STATE_Waiting)
{
PhoneEntry *ChosenEntry;
struct List *ChosenPattern;
ULONG Cost;
/* Terminate the timer request */
WaitIO((struct IORequest *)TimeRequest);
/* We are no longer waiting */
State = STATE_Idle;
/* Start with a clean slate */
Cost = 0;
/* We want to read the accounting data */
ChosenPattern = LockActivePattern();
ChosenEntry = LockActiveEntry(GlobalPhoneHandle);
if(ChosenEntry || ChosenPattern)
{
/* Look up the current time */
GetSysTime(&Now);
/* Check which rate data is current */
SelectTime(ChosenEntry,ChosenPattern,&Now);
/* Store the cost for the next unit */
Cost = PayPerUnit[DT_NEXT_UNIT];
/* Does this unit last for a certain time? */
if(SecPerUnit[DT_NEXT_UNIT] > 0)
{
struct timeval TimeVal;
/* This is the time it takes for the */
/* unit to elapse */
TimeVal.tv_secs = SecPerUnit[DT_NEXT_UNIT] / 10000;
TimeVal.tv_micro = (SecPerUnit[DT_NEXT_UNIT] % 10000) * 100;
/* Remember when the unit will have elapsed */
GetSysTime(&StopTime);
AddTime(&StopTime,&TimeVal);
/* Start waiting again */
TimeRequest->tr_node.io_Command = TR_ADDREQUEST;
TimeRequest->tr_time = TimeVal;
SendIO((struct IORequest *)TimeRequest);
State = STATE_Waiting;
}
}
UnlockActiveEntry(GlobalPhoneHandle);
UnlockActivePattern();
/* Add up the connection cost */
ObtainSemaphore(&AccountantSemaphore);
AccountantCost += Cost;
ReleaseSemaphore(&AccountantSemaphore);
}
/* Somebody wants us to do something... */
if(Signals & PORTMASK(AccountantPort))
{
AccountantMsg *Message;
/* For each message... */
while(Message = (AccountantMsg *)GetMsg(AccountantPort))
{
switch(Message->Type)
{
/* Start waiting? */
case ACMSG_Start:
/* Remember the time when this unit */
/* will have elapsed */
GetSysTime(&StopTime);
AddTime(&StopTime,&Message->Time);
/* Start waiting */
TimeRequest->tr_node.io_Command = TR_ADDREQUEST;
TimeRequest->tr_time = Message->Time;
SendIO((struct IORequest *)TimeRequest);
State = STATE_Waiting;
break;
/* Stop waiting? */
case ACMSG_Stop:
/* Are we currently waiting? */
if(State == STATE_Waiting)
{
if(!CheckIO((struct IORequest *)TimeRequest))
AbortIO((struct IORequest *)TimeRequest);
WaitIO((struct IORequest *)TimeRequest);
State = STATE_Idle;
}
break;
/* Return the time that still has to elapse? */
case ACMSG_Query:
if(State == STATE_Waiting)
{
/* This is now */
GetSysTime(&Now);
/* Is there still time left */
/* before the unit elapses? */
if(CmpTime(&StopTime,&Now) < 0) /* i.e. "StopTime > Now" */
{
/* Return the remaining time */
Message->Time = StopTime;
SubTime(&Message->Time,&Now);
break;
}
}
/* Clear the time value */
memset(&Message->Time,0,sizeof(Message->Time));
break;
}
/* Return the message */
ReplyMsg((struct Message *)Message);
}
}
}
/* Are we still waiting? */
if(State == STATE_Waiting)
{
if(!CheckIO((struct IORequest *)TimeRequest))
AbortIO((struct IORequest *)TimeRequest);
WaitIO((struct IORequest *)TimeRequest);
}
/* Start cleaning up... */
DeleteMsgPort(AccountantPort);
}
CloseDevice((struct IORequest *)TimeRequest);
}
DeleteIORequest((struct IORequest *)TimeRequest);
}
DeleteMsgPort(TimePort);
}
/* Finished; wave goodbye and exit */
Forbid();
AccountantTask = NULL;
Signal((struct Task *)ThisProcess,SIG_HANDSHAKE);
}
/****************************************************************************/
/* DeleteAccountant():
*
* Stop the accounting task.
*/
VOID
DeleteAccountant()
{
ShakeHands(AccountantTask,SIG_KILL);
}
/* CreateAccountant():
*
* Launch the accounting task.
*/
BOOL
CreateAccountant()
{
InitSemaphore(&AccountantSemaphore);
Forbid();
if(LocalCreateTask("term Accountant Task",ThisProcess->pr_Task.tc_Node.ln_Pri + 10,(TASKENTRY)AccountantEntry,4096,0))
WaitForHandshake();
Permit();
return((BOOL)(AccountantTask != NULL));
}
/****************************************************************************/
/* QueryAccountantTime(struct timeval *Time):
*
* Query the time that will have to elapse before
* the next unit will begin.
*/
ULONG
QueryAccountantTime(struct timeval *Time)
{
AccountantMsg Message;
memset(&Message,0,sizeof(Message));
Message.Message.mn_Length = sizeof(Message);
Message.Type = ACMSG_Query;
SendMessageGetReply(AccountantPort,(struct Message *)&Message);
if(Time)
{
Time->tv_secs = Message.Time.tv_secs;
Time->tv_micro = Message.Time.tv_micro;
}
return(Message.Time.tv_secs);
}
/* QueryAccountantCost():
*
* Query the cost of the current connection.
*/
ULONG
QueryAccountantCost()
{
ULONG Cost;
SafeObtainSemaphoreShared(&AccountantSemaphore);
Cost = AccountantCost;
ReleaseSemaphore(&AccountantSemaphore);
return(Cost);
}
/* StopAccountant():
*
* Stop cost accounting and return the current
* connection cost.
*/
ULONG
StopAccountant()
{
AccountantMsg Message;
ULONG Cost;
memset(&Message,0,sizeof(Message));
Message.Message.mn_Length = sizeof(Message);
Message.Type = ACMSG_Stop;
SendMessageGetReply(AccountantPort,(struct Message *)&Message);
SafeObtainSemaphoreShared(&AccountantSemaphore);
Cost = AccountantCost;
ReleaseSemaphore(&AccountantSemaphore);
return(Cost);
}
/* StartAccountant(ULONG OnlineSeconds):
*
* Start cost accounting for the current connection.
*/
VOID
StartAccountant(ULONG OnlineSeconds)
{
PhoneEntry *ChosenEntry;
struct List *ChosenPattern;
struct timeval Remain;
struct timeval Now;
ULONG Cost;
ChosenPattern = LockActivePattern();
ChosenEntry = LockActiveEntry(GlobalPhoneHandle);
/* Forget the old data */
StopAccountant();
/* Now is the time for all good men... */
GetSysTime(&Now);
/* Did the connection happen a few seconds earlier? */
if(OnlineSeconds > 0)
{
struct timeval Lead;
/* Take care of the leading time */
Lead.tv_secs = OnlineSeconds;
Lead.tv_micro = 0;
/* Technically, the connection happened a bit earlier. */
SubTime(&Now,&Lead);
/* Choose the right rate accounting time. */
SelectTime(ChosenEntry,ChosenPattern,&Now);
/* Enter the first rate. */
Cost = PayPerUnit[DT_FIRST_UNIT];
Remain.tv_secs = SecPerUnit[DT_FIRST_UNIT] / 10000;
Remain.tv_micro = (SecPerUnit[DT_FIRST_UNIT] % 10000) * 100;
/* Did the first unit elapse already? */
while(-CmpTime(&Lead,&Remain) >= 0 && (Remain.tv_secs || Remain.tv_micro)) /* "Lead >= Remain" */
{
/* Subtract the unit time */
SubTime(&Lead,&Remain);
/* Update the "current" time so SelectTime */
/* can do something sensible. */
AddTime(&Now,&Remain);
SelectTime(ChosenEntry,ChosenPattern,&Now);
Remain.tv_secs = SecPerUnit[DT_NEXT_UNIT] / 10000;
Remain.tv_micro = (SecPerUnit[DT_NEXT_UNIT] % 10000) * 100;
/* In any case, a new unit starts here */
Cost += PayPerUnit[DT_NEXT_UNIT];
}
/* Adjust the remaining time it takes for the unit */
/* to elapse. */
SubTime(&Remain,&Lead);
}
else
{
/* Choose the right rate accounting time. */
SelectTime(ChosenEntry,ChosenPattern,&Now);
/* Enter the first rate. */
Cost = PayPerUnit[DT_FIRST_UNIT];
Remain.tv_secs = SecPerUnit[DT_FIRST_UNIT] / 10000;
Remain.tv_micro = (SecPerUnit[DT_FIRST_UNIT] % 10000) * 100;
}
/* Store the cost */
ObtainSemaphore(&AccountantSemaphore);
AccountantCost = Cost;
ReleaseSemaphore(&AccountantSemaphore);
/* That's it; "Remain" now holds the time to wait before */
/* the first unit elapses. Careful here, we must not */
/* launch a `zero' time wait request. */
if(Remain.tv_secs > 0 || Remain.tv_micro > 0)
{
AccountantMsg Message;
memset(&Message,0,sizeof(Message));
Message.Message.mn_Length = sizeof(Message);
Message.Type = ACMSG_Start;
Message.Time = Remain;
SendMessageGetReply(AccountantPort,(struct Message *)&Message);
}
UnlockActiveEntry(GlobalPhoneHandle);
UnlockActivePattern();
}